﻿using System.Diagnostics.Contracts;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace Arachnophile
{
    /// <summary>
    /// XML processing with QName insensitivity.
    /// </summary>
    public static class InfosetLocalEx
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static IEnumerable<XElement> LElements(this XElement source, string name)
        {
            Contract.Requires(source != null);
            Contract.Requires(name != null);
            Contract.Ensures(Contract.Result<IEnumerable<XElement>>() != null);

            return from e in source.Elements()
                   where e.Name.LocalName == name
                   select e;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="names"></param>
        /// <returns></returns>
        public static IEnumerable<XElement> LElements(this XElement source, params string[] names)
        {
            Contract.Requires(source != null);
            Contract.Ensures(Contract.Result<IEnumerable<XElement>>() != null);

            //...

            return source == null
                ? null
                : names.Length == 0
                  ? Enumerable.Empty<XElement>()
                  : names.Length == 1
                    ? source.LElements(names.First())
                    : source.LElement(names.First()).LElements(names.Skip(1).ToArray());
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static XElement LElement(this XElement source, string name)
        {
            Contract.Requires(source != null);
            Contract.Requires(name != null);
            Contract.Ensures(Contract.Result<XElement>() != null);

            return source.LElements(name).FirstOrDefault();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static IEnumerable<XElement> LElements(this IEnumerable<XElement> source, string name)
        {
            Contract.Requires(source != null);
            Contract.Requires(name != null);
            Contract.Ensures(Contract.Result<IEnumerable<XElement>>() != null);

            return from es in source
                   from e in es.LElements(name)
                   select e;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static XElement LElement(this IEnumerable<XElement> source, string name)
        {
            Contract.Requires(source != null);
            Contract.Requires(name != null);
            Contract.Ensures(Contract.Result<XElement>() != null);

            return source.LElements(name).FirstOrDefault();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static IEnumerable<XAttribute> LAttributes(this XElement source, string name)
        {
            Contract.Requires(source != null);
            Contract.Requires(name != null);
            Contract.Ensures(Contract.Result<IEnumerable<XAttribute>>() != null);

            return from a in source.Attributes()
                   where a.Name.LocalName == name
                   select a;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public static IEnumerable<XAttribute> LAttributes(this IEnumerable<XElement> source, string name)
        {
            Contract.Requires(source != null);
            Contract.Requires(name != null);
            Contract.Ensures(Contract.Result<IEnumerable<XAttribute>>() != null);

            return from es in source.Elements()
                   from a in es.LAttributes(name)
                   select a;
        }
    }
}
